package com.cxb.gsensor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class DrivingHehaviorAnalysiser3 {

	private float alpha = 1 / 256.0f; // µÍÍ¨ÂË²¨Æ÷²ÎÊý
	private float rate = 0.02f;// ²ÉÑùÖÜÆÚ
	private float threshold = 0.8f;// ãÐÖµ
	private float account = 0.2f;// ³¬¹ýÃÅÏÞ²¿·Ö»ý·ÖãÐÖµ
	private float window = 1.2f;// »ý·Ö´°¿Ú

	DataStack orgData = new DataStack();
	DataStack middleValueFilterData = new DataStack();
	DataStack lowPassFilterData = new DataStack();
	DataStack integralData = new DataStack();

	private float secondAccIntegral = 0;
	private float secondDecIntegral = 0;
	private DriverResultCallBack driverResultCallBack;
	private float currentSpeed;
	private float speedThreshold = 10;// 速度阀值 m/s
	public static final float INTERVAL = 0.02f; // 时间间隔
	public enum SpeedMode {
		HIGHT, LOW
	}

	public enum DriverEvent {
		ACC, DEC
	}

	private DriverEvent detectMode;

	public void setDetectMode(DriverEvent driverEvent) {
		detectMode = driverEvent;
		if (driverEvent == DriverEvent.ACC) {
			// ���ٸ���50
			threshold = 1.2f;
			account = 3.0f;
			window = 1.5f;
		} else if (driverEvent == DriverEvent.DEC) {
			// ���ٵ���50

			threshold = 1.2f;
			account = 1.5f;
			window = 1.0f;

		}
		init();
	}

	public void setCurrentSpeed(float speed) {
		this.currentSpeed = speed;
	}

	public DrivingHehaviorAnalysiser3() {
		init();
	}

	public void setCallBack(DriverResultCallBack callback) {
		this.driverResultCallBack = callback;
	}

	private void init() {
		integralData.setMaxSize((int) (window / rate));
		middleValueFilterData.setMaxSize((int) (window / rate) + 1);
		orgData.setMaxSize(5);
		lowPassFilterData.setMaxSize(5);
	}

	public void pushData(float data) {
		orgData.addData(data);

		// µÍÍ¨ÂË²¨
		if (orgData.isOnlyOne()) {
			lowPassFilterData.addData(orgData.getFirst());
		} else {
			lowPassFilterData
					.addData(lowPassFilter(data, orgData.getLastSecond(),
							lowPassFilterData.getLast(), alpha));
		}

		// ÖÐÖµÂË²¨
		if (orgData.getSize() < 5) {
			middleValueFilterData.addData(orgData.getLast()
					- lowPassFilterData.getLast());
		} else {

			float[] mvdPreData = {
					orgData.get(orgData.getSize() - 1)
							- lowPassFilterData.get(orgData.getSize() - 1),
					orgData.get(orgData.getSize() - 2)
							- lowPassFilterData.get(orgData.getSize() - 2),
					orgData.get(orgData.getSize() - 3)
							- lowPassFilterData.get(orgData.getSize() - 3),
					orgData.get(orgData.getSize() - 4)
							- lowPassFilterData.get(orgData.getSize() - 4),
					orgData.get(orgData.getSize() - 5)
							- lowPassFilterData.get(orgData.getSize() - 5) };
			float middleValue = middleValueFilter(mvdPreData);
			middleValueFilterData.addData(middleValue);

		}

		// µÚÒ»´Î»ý·Ö

		if (middleValueFilterData.isOnlyOne()) {
			integralData.addData(0);
		} else {
			float firstInteGralResult = 0;
			for (int i = 0; i < middleValueFilterData.getSize() - 1; i++) {
				firstInteGralResult += (middleValueFilterData
						.get(middleValueFilterData.getSize() - i - 1) + middleValueFilterData
						.get(middleValueFilterData.getSize() - i - 2))
						* rate * 0.5f;

			}
			integralData.addData(-firstInteGralResult);
		}

		// System.out.println(middleValueFilterData.getLast()+"   "+integralData.getLast()+"   "+integralData.getSize());
		if (integralData.getLast() > threshold) {
			// ¼±¼ÓËÙµÚÒ»¸öÌõ¼þ
			float tempIntegral = (float) (0.5f * (integralData.getLast()
					+ integralData.getLastSecond() - 2 * threshold) * rate);
			secondAccIntegral += tempIntegral;
		} else {
			if (secondAccIntegral >= account) {
				System.out.println(secondAccIntegral);
				System.out.println("������");
				if (driverResultCallBack != null
						&& DriverEvent.ACC == detectMode) {
					if (currentSpeed >= speedThreshold) {
						driverResultCallBack.onEvent(DriverEvent.ACC);
					}
				}
			}

			secondAccIntegral = 0;
		}

		if (integralData.getLast() < -threshold) {
			// ¼±¼õËÙµÚÒ»¸öÌõ¼þ

			float tempIntegral = (float) (0.5 * (integralData.getLast()
					+ integralData.getLastSecond() + 2 * threshold) * rate);
			secondDecIntegral += tempIntegral;

		} else {
			if (secondDecIntegral <= -account) {
				System.out.println("������");
				if (driverResultCallBack != null
						&& DriverEvent.DEC == detectMode) {
					if (currentSpeed >= speedThreshold) {
						driverResultCallBack.onEvent(DriverEvent.DEC);
					}
				}
			}
			secondDecIntegral = 0;

		}

	}

	/**
	 * 
	 * @param nowDataµ±Ç°Öµ
	 * @param preOrgData
	 *            ÉÏÒ»¸öÔ­Ê¼Öµ
	 * @param preLpgData
	 *            ÉÏÒ»¸öµÍÍ¨ÂË²¨ºóµÄÖµ
	 * @param filterArg
	 *            µÍÍ¨ÂË²¨²ÎÊý
	 * @return
	 */
	public float lowPassFilter(float nowData, float preOrgData,
			float preLpgData, float alpha) {

		float result = alpha / 2 * nowData + alpha / 2 * preOrgData
				+ (1 - alpha) * preLpgData;
		return result;
	}

	/**
	 * ÖÐÖµÂË²¨
	 * 
	 * @param values
	 *            ÂË²¨Êý¾Ý(ÖÁÉÙ´óÓÚ2)
	 * @return
	 */
	public float middleValueFilter(float... values) {
		Arrays.sort(values);
		return values[values.length / 2];
	}

	class DataStack {

		private List<Float> list = new ArrayList<Float>();

		private int maxSize;

		public void setMaxSize(int max) {
			this.maxSize = max;
		};

		public void setData(int index, float data) {
			if (index < list.size()) {
				list.remove(index);
				list.add(index, data);
			} else {
				for (int i = list.size() - 1; i < index + 1; i++) {
					list.add(null);
				}
				list.set(index, data);
			}

			if (list.size() > maxSize) {
				list.remove(0);
			}

		}

		public void addData(float data) {
			list.add(data);
			if (list.size() > maxSize) {
				list.remove(0);
			}
		}

		public void clearData() {
			list.clear();
		}

		public boolean isOnlyOne() {
			return list.size() == 1;
		}

		public float get(int index) {
			return list.get(index);
		}

		public float getFromLast(int lastIndex) {
			return list.get(list.size() - lastIndex - 1);
		}

		public float getLast() {
			return list.get(list.size() - 1);
		}

		public Float[] getRange(int fromIndex, int toIndex) {
			Float[] result = new Float[] {};
			return list.subList(fromIndex, toIndex).toArray(result);
		}

		public float getFirst() {
			return list.get(0);
		}

		public int getSize() {
			return list.size();
		}

		public float getLastSecond() {
			return list.get(list.size() - 2);
		}

		public boolean isEmpty() {
			return list.size() == 0;
		}

	}

	public interface DriverResultCallBack {
		public void onEvent(DriverEvent event);
	}

}
